package com.huangd.platform.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.huangd.platform.config.entity.BaseRedisEntity;
import com.huangd.platform.config.entity.LadderRedisEntity;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.MapPropertySource;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/*** 功能描述: 多集群数据源配置
 */
@Configuration
public class RedisConfig {

    @Resource
    BaseRedisEntity baseRedisEntity;

    @Resource
    LadderRedisEntity ladderRedisEntity;

    /**
     * 配置lettuce连接池
     */
    @Bean
    @Primary
    @ConfigurationProperties (prefix = "spring.redis.cluster.lettuce.pool")
    public GenericObjectPoolConfig redisPool() {
        return new GenericObjectPoolConfig();
    }

    /**
     * 配置第一个数据源的
     */
    @Bean ("redisClusterConfig")
    @Primary
    public RedisClusterConfiguration redisClusterConfig() {

        // 查询redis 的链接
        String redisHost = baseRedisEntity.getHost();
        String redisPwd = baseRedisEntity.getPwd();
        String redisPort = baseRedisEntity.getPort();

        Map<String, Object> source = new HashMap<>(8);
        source.put("spring.redis.cluster.nodes", redisHost + ":" + redisPort);
        RedisClusterConfiguration redisClusterConfiguration;
        redisClusterConfiguration = new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration",
                source));
        //redisClusterConfiguration.setPassword(redisPwd);
        return redisClusterConfiguration;

    }

    /**
     * 配置第一个数据源的连接工厂
     * 这里注意：需要添加@Primary 指定bean的名称，目的是为了创建两个不同名称的LettuceConnectionFactory
     */
    @Bean ("lettuceConnectionFactory")
    @Primary
    public LettuceConnectionFactory lettuceConnectionFactory(GenericObjectPoolConfig redisPool, @Qualifier (
            "redisClusterConfig") RedisClusterConfiguration redisClusterConfig) {
        LettuceClientConfiguration clientConfiguration =
                LettucePoolingClientConfiguration.builder().poolConfig(redisPool).build();
        return new LettuceConnectionFactory(redisClusterConfig, clientConfiguration);
    }

    /**
     * 配置第一个数据源的RedisTemplate
     * 注意：这里指定使用名称=factory 的 RedisConnectionFactory
     * 并且标识第一个数据源是默认数据源 @Primary
     */
    @Bean ("redisTemplate")
    @Primary
    public RedisTemplate redisTemplate(@Qualifier ("lettuceConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
        return getRedisTemplate(redisConnectionFactory);

    }

    /**
     * 配置第二个数据源
     */
    @Bean ("ladderRedisConfig")
    public RedisClusterConfiguration ladderRedisConfig() {

        // 查询redis 的链接
        // 查询redis 的链接
        String redisHost = ladderRedisEntity.getHost();
        String redisPwd = ladderRedisEntity.getPwd();
        String redisPort = ladderRedisEntity.getPort();

        Map<String, Object> source = new HashMap<>(8);
        source.put("spring.redis.cluster.nodes", redisHost + ":" + redisPort);
        RedisClusterConfiguration redisClusterConfiguration;
        redisClusterConfiguration = new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration",
                source));
        //redisClusterConfiguration.setPassword(redisPwd);

        return redisClusterConfiguration;
    }

    @Bean ("ladderLettuceConnectionFactory")
    public LettuceConnectionFactory ladderLettuceConnectionFactory(GenericObjectPoolConfig redisPool, @Qualifier (
            "ladderRedisConfig") RedisClusterConfiguration ladderRedisClusterConfig) {
        LettuceClientConfiguration clientConfiguration =
                LettucePoolingClientConfiguration.builder().poolConfig(redisPool).build();
        return new LettuceConnectionFactory(ladderRedisClusterConfig, clientConfiguration);
    }

    /**
     * 配置第一个数据源的RedisTemplate
     * 注意：这里指定使用名称=factory2 的 RedisConnectionFactory
     */
    @Bean ("ladderRedisTemplate")
    public RedisTemplate ladderRedisTemplate(@Qualifier ("ladderLettuceConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
        return getRedisTemplate(redisConnectionFactory);
    }


    /*** 功能描述: 根据连接工厂获取一个RedisTemplate
     */
    private RedisTemplate getRedisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        // json 序列化配置
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 识别所有字段 PropertyAccessor 识别任何字段 JsonAutoDetect
        // objectMapper.enableDefaultTyping();  // enableDefaultTyping 已过期
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(objectMapper);


        // String 的 序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // key 采用 String的序列化
        template.setKeySerializer(stringRedisSerializer);
        // hash 的key采用String的序列化
        template.setHashKeySerializer(stringRedisSerializer);
        // hash 的 value 采用 String 的序列化
        template.setHashValueSerializer(stringRedisSerializer);
        // value 序列化方式采用 Jackson
        template.setValueSerializer(stringRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

}